package OssAliyun;

import cn.hutool.crypto.asymmetric.RSA;
import com.aliyun.oss.ClientException;
import com.aliyun.oss.OSSEncryptionClient;
import com.aliyun.oss.OSSEncryptionClientBuilder;
import com.aliyun.oss.OSSException;
import com.aliyun.oss.common.auth.CredentialsProviderFactory;
import com.aliyun.oss.common.auth.DefaultCredentialProvider;
import com.aliyun.oss.crypto.SimpleRSAEncryptionMaterials;
import com.aliyun.oss.model.OSSObject;

import java.io.*;
import java.security.KeyPair;
import java.security.interfaces.RSAPrivateKey;
import java.security.interfaces.RSAPublicKey;
import java.util.HashMap;
import java.util.Map;

/**
 * @author 航
 * @date 2024/06/07 15:08
 **/
public class ApplicationEncryption {

    private final static String endpoint = "https://oss-cn-hangzhou.aliyuncs.com";
    private final static String region = "cn-hangzhou";
    private final static String accessKeyId = "LTAI5tQnCGJCP9sf1dT8SNsk";
    private final static String accessKeySecret = "O5I4hOtBhZowjMQmcF2suUSGz18Tx2";
    private final static String bucketName = "hang-cs-test-bucket";

    /**
     * 文件加密后上传
     * @param file 需要上传的文件
     * @param objectPath 放入OSS中的路径，包含文件名
     * @param filePath 下载到本地文件的路径，包含文件名（路径必须存在）
     * @throws Exception 异常抛出
     */
    public void uploadFile(File file, String objectPath, String filePath) throws Exception{
        DefaultCredentialProvider credentialsProvider =
                CredentialsProviderFactory.newDefaultCredentialProvider(accessKeyId,accessKeySecret);

        // 通过 hutool 工具获得RSA私钥和公钥
        RSA rsa = new RSA();
        // 填写您的RSA私钥字符串
        final String PRIVATE_PKCS1_PEM = rsa.getPrivateKeyBase64();
        // 填写您的RSA公钥字符串
        final String PUBLIC_X509_PEM = rsa.getPublicKeyBase64();

        // 创建一个RSA密钥对。
        RSAPrivateKey privateKey = SimpleRSAEncryptionMaterials.getPrivateKeyFromPemPKCS8(PRIVATE_PKCS1_PEM);
        RSAPublicKey publicKey = SimpleRSAEncryptionMaterials.getPublicKeyFromPemX509(PUBLIC_X509_PEM);
        KeyPair keyPair = new KeyPair(publicKey, privateKey);

        // 创建主密钥RSA的描述信息。创建后不允许修改。主密钥描述信息和主密钥一一对应。
        // 如果所有的object都使用相同的主密钥，主密钥描述信息可以为空，但后续不支持更换主密钥。
        // 如果主密钥描述信息为空，解密时无法判断文件使用的是哪个主密钥进行加密。
        // 强烈建议为每个主密钥都配置描述信息，由客户端保存主密钥和描述信息之间的对应关系（服务端不保存两者之间的对应关系）。
        Map<String, String> matDesc = new HashMap<String, String>();
        matDesc.put("desc-key", "desc-value");

        // 创建RSA加密材料。
        SimpleRSAEncryptionMaterials encryptionMaterials = new SimpleRSAEncryptionMaterials(keyPair, matDesc);
        // 如果要下载并解密其他RSA密钥加密的文件，请将其他主密钥及其描述信息添加到加密材料中。
        // encryptionMaterials.addKeyPairDescMaterial(<otherKeyPair>, <otherKeyPairMatDesc>);

        // 创建加密客户端。
        OSSEncryptionClient ossEncryptionClient = new OSSEncryptionClientBuilder().
                build(endpoint, credentialsProvider, encryptionMaterials);

        try {
            // 加密上传文件。
            ossEncryptionClient.putObject(bucketName, objectPath, new FileInputStream(file));

            // 下载文件时自动解密。
            OSSObject ossObject = ossEncryptionClient.getObject(bucketName, objectPath);
            try (FileOutputStream outputStream = new FileOutputStream(new File(filePath))) {
                byte[] buffer = new byte[1024];
                int bytesRead;
                while ((bytesRead = ossObject.getObjectContent().read(buffer)) != -1) {
                    outputStream.write(buffer, 0, bytesRead);
                }
            }

        } catch (OSSException oe) {
            System.out.println("Caught an OSSException, which means your request made it to OSS, "
                    + "but was rejected with an error response for some reason.");
            System.out.println("Error Message:" + oe.getErrorMessage());
            System.out.println("Error Code:" + oe.getErrorCode());
            System.out.println("Request ID:" + oe.getRequestId());
            System.out.println("Host ID:" + oe.getHostId());
        } catch (ClientException ce) {
            System.out.println("Caught an ClientException, which means the client encountered "
                    + "a serious internal problem while trying to communicate with OSS, "
                    + "such as not being able to access the network.");
            System.out.println("Error Message:" + ce.getMessage());
        } finally {
            if (ossEncryptionClient != null) {
                ossEncryptionClient.shutdown();
            }
        }
    }
}
